home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / lang_oth / forchek1 / forchek.c < prev    next >
C/C++ Source or Header  |  1991-11-05  |  23KB  |  870 lines

  1. /*  forchek.c:
  2.  
  3.  Main program for Fortran Syntax Checker.
  4.  
  5.     Copyright (C) 1991 by Robert K. Moniot.
  6.     This program is free software.  Permission is granted to
  7.     modify it and/or redistribute it, retaining this notice.
  8.     No guarantees accompany this software.
  9.  
  10.  
  11.  Top-level input/output is done here: opening and closing files,
  12.  and printing error, warning, and informational messages.
  13.  
  14.  Shared functions defined:
  15.   print_a_line() Prints source code line.
  16.   yyerror() Error messages from yyparse and elsewhere.
  17.   syntax_error() Error messages with line and column num.
  18.   warning() Warning messages.
  19.   nonportable() Portability warnings.
  20.   wrapup() Look at cross references, etc.
  21. */
  22.  
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <ctype.h>
  26. #define MAIN
  27. #include "forchek.h"
  28.  
  29. /* char *getenv(),*malloc(); */
  30. char *getenv();
  31. void* malloc(unsigned);
  32. void exit();
  33.  
  34. PRIVATE void src_file_in(),
  35. error_message(), error_summary(), get_env_options(),
  36. make_env_name(), set_option(), list_options(), open_outfile(),
  37. resource_summary(), wrapup();
  38.  
  39. PRIVATE int read_setting();
  40.  
  41. PRIVATE int project_file_input, /* true if input is from .prj file */
  42.      full_output; /* = (verbose || do_list | do_symtab) */
  43.  
  44. unsigned long intrins_clashes; /* count of intrinsic hashtable clashes */
  45. #ifdef COUNT_REHASHES
  46. extern unsigned long rehash_count; /* count of calls to rehash() */
  47. #endif
  48.  
  49.  /* Here we define the commandline options.  Most options are boolean
  50.     switchopts, with "no" prefix to unset them.  Others (called
  51.     settings) are numeric quantities, defined using "=num".
  52.     A third category (strsettings) are string quantities, eg filenames.
  53.     The argument "?" will cause list of options to be printed out.
  54.     For VMS, options can be prefixed with either "-" or "/",
  55.     but messages will use the canonical form. */
  56.  
  57. #ifdef OPTION_PREFIX_SLASH
  58. #define OPT_PREFIX '/' /* Canonical VMS prefix for commandline options */
  59. #else
  60. #define OPT_PREFIX '-' /* Canonical Unix prefix for commandline options */
  61. #endif
  62.  
  63. #define OPT_MATCH_LEN 3 /* Options are matched only in 1st 3 chars */
  64. #define NUM_SWITCHES (sizeof(switchopt)/sizeof(switchopt[0]))
  65. #define NUM_SETTINGS (sizeof(setting)/sizeof(setting[0]))
  66. #define NUM_STRSETTINGS (sizeof(strsetting)/sizeof(strsetting[0]))
  67.  
  68. /* Option definitions:
  69.     New options can be added to lists by inserting definition
  70.     here using same syntax as others, and declaring the variable
  71.     (twice!) in forchek.h.  No other changes needed.
  72. */
  73.  
  74.  
  75.   /* List of switches is defined first.  Each entry gives the
  76.      name and the corresponding flag variable to be set
  77.      or cleared.  See set_option() for processing of switches.
  78.  
  79.      N.B. list_options() will suppress printing of any options
  80.      whose explanation starts with "debug" unless the -debug
  81.      switch was previously given.
  82.    */
  83. struct {
  84.     char *name;
  85.     int *switchflag;
  86.     char *explanation;
  87. } switchopt[]={
  88.  {"declare", &decls_required, "list undeclared variables"},
  89.  {"division", &div_check,  "catch possible div by 0"},
  90.  {"extern", &ext_def_check,  "check if externals defined"},
  91.  {"f77",  &f77_standard, "warn of nonstandard constructs"},
  92.  {"library", &library_mode,  "treat next files as library"},
  93.  {"linebreak", &eol_is_space,  "treat linebreaks as space"},
  94.  {"list", &do_list,  "print program listing"},
  95.  {"portability", &port_check,  "check portability"},
  96.  {"project", &make_project_file, "create project file"},
  97.  {"symtab", &do_symtab,  "print symbol table info"},
  98.  {"sixchar", &sixclash,  "catch nonunique names"},
  99.  {"usage", &usage_check,  "check var set/used status"},
  100.  {"verbose", &verbose,  "verbose output"},
  101. /* {"version", &print_version_num, "print version number"},*/
  102.  {"debug", &debug_latest,  "debug latest code"},
  103.  {"global", &debug_glob_symtab, "debug global symtab info"},
  104.  {"grammar", &debug_parser,  "debug printout in parser"},
  105.  {"yydebug", &yydebug,  "debug via yydebug"},
  106.  {"hashtable", &debug_hashtab,  "debug printout of hashtable"},
  107.  {"local", &debug_loc_symtab, "debug local symtab info"},
  108.  {"resources", &show_resources, "debug info on resources"},
  109.  {"tokens", &debug_lexer,  "debug printout in lexer"},
  110. };
  111.  
  112.  
  113.   /* List of settings is defined here. Each entry gives
  114.      the name, the corresponding variable, and the range
  115.      of permitted values, followed by brief explanation.
  116.      See set_option() for processing. */
  117. struct {
  118.     char *name;
  119.     int *setvalue;
  120.     int minlimit,maxlimit;
  121.     char *explanation;
  122. } setting[]={
  123.   {"columns", &max_stmt_col, 1, MAXLINE, "max line length processed"},
  124.   {"common", &comcheck_strictness,  0, 3,"common check: 0=none, 3=strict"},
  125.   {"novice", &novice_level,  1, 5, "novice level: 1=green, 5=wizard"},
  126. };
  127.  
  128.  
  129.   /* List of strsettings is defined here. Each entry gives
  130.      the name the corresponding string variable, and brief
  131.      explanation.  See set_option() for processing. */
  132. struct {
  133.     char *name;
  134.     char **strvalue;
  135.     char *explanation;
  136. } strsetting[]={
  137.   {"output", &out_fname, "output file name"},
  138. };
  139.  
  140. int must_open_outfile=FALSE; /* Flag set to TRUE when out=name given */
  141.  
  142. int
  143. main(argc,argv)
  144.  int argc;
  145.  char *argv[];
  146. {
  147.  int iarg;
  148.  int filecount=0,actioncount=0;
  149.  char *infile,*srcfile,*projfile;
  150.  
  151.  list_fd = stdout;
  152.  project_fd = NULL;
  153.  error_count = 0;
  154.  warning_count = 0;
  155.  
  156.  get_env_options();
  157.  
  158.  init_keyhashtab();  /* Initialize tables */
  159.  intrins_clashes = init_intrins_hashtab();
  160.  init_globals();
  161.  init_symtab();
  162.  
  163.  for(iarg=1; iarg < argc; iarg++) {
  164.  
  165.    /* Process flags here */
  166.      if( argv[iarg][0] == '-'
  167. #ifdef OPTION_PREFIX_SLASH
  168.    || argv[iarg][0] == '/' /* Allow VMS /option form */
  169. #endif
  170.       ) {
  171.   set_option(argv[iarg]);
  172.  
  173.      }
  174.  
  175.      else { /* Process '?' and file arguments */
  176.  
  177.          full_output = verbose || do_list || do_symtab;
  178.  
  179.   if( must_open_outfile )
  180.       open_outfile(out_fname);
  181.  
  182.   if(actioncount == 0) {
  183.       fprintf(list_fd,"%s%s%s",
  184.        full_output?"\n":"",
  185.        VERSION_NUMBER,
  186.        full_output?"\n":"");
  187.   }
  188.   ++actioncount; /* Cause exit w/o reading stdin below */
  189.  
  190.   if(strcmp(argv[iarg],"?") == 0) {
  191.       list_options();
  192.   }
  193.   else {  /* Process files here */
  194.       ++filecount;
  195.  
  196.       srcfile = add_ext(argv[iarg],DEF_SRC_EXTENSION);
  197.       projfile = new_ext(argv[iarg],DEF_PROJ_EXTENSION);
  198.  
  199.     /* Project file mode: open source for reading
  200.        and .prj file for writing. */
  201.       if(make_project_file) {
  202.  
  203.         infile = srcfile;
  204.  
  205.         if( has_extension(infile,DEF_PROJ_EXTENSION) ) {
  206.    fprintf(stderr,
  207.     "Input from %s disallowed in project mode\n",infile);
  208.    continue;
  209.         }
  210.  
  211.         if( (input_fd = fopen(infile,"r")) == NULL ) {
  212.    fprintf(stderr,"Cannot open file %s\n",infile);
  213.    continue;
  214.         }
  215.  
  216.         project_fd = fopen(projfile,"w");
  217.         project_file_input = FALSE;
  218.       }
  219.       else {
  220.                  /* Non project file mode: if input file extension
  221.       given, use it.  Otherwise read project file
  222.       if it exists else read source file. */
  223.         if( argv[iarg]==srcfile
  224.          || (input_fd = fopen(projfile,"r")) == NULL) {
  225.    infile = srcfile;
  226.    if( (input_fd = fopen(infile,"r")) == NULL ) {
  227.      fprintf(stderr,"Cannot open file %s\n",infile);
  228.      continue;
  229.    }
  230.    project_file_input =
  231.      has_extension(infile,DEF_PROJ_EXTENSION);
  232.         }
  233.         else {
  234.    infile = projfile;
  235.    project_file_input = TRUE;
  236.         }
  237.       }
  238.  
  239.     /* Always print input .f file name.  If
  240.        verbose mode, print .prj file names too.
  241.        In verbose mode, add blank line
  242.        and after file name. */
  243.       if(verbose || !project_file_input)
  244.         fprintf(list_fd,"\nFile %s:%s",
  245.          infile,
  246.          full_output?"\n":""
  247.          );
  248.  
  249.     /* In verbose mode, print .prj output
  250.        file name to stderr.  Always print
  251.        error message if couldn't open it. */
  252.       if( make_project_file ) {
  253.         if(project_fd != NULL) {
  254.    if(verbose)
  255.      fprintf(stderr,
  256.       "\nProject file is %s\n",projfile);
  257.         }
  258.         else {
  259.    fprintf(stderr,
  260.     "\nCannot open %s for output\n",projfile);
  261.         }
  262.       }
  263.  
  264.  
  265.       if(project_file_input) {
  266.  
  267.    proj_file_in(input_fd);
  268.  
  269.       }
  270.       else {
  271.  
  272.         src_file_in(infile);
  273.  
  274.       }
  275.  
  276.       (void) fclose(input_fd);
  277.     }
  278.  
  279.      }
  280.  } /* end for-loop on argument list */
  281.  
  282.  if(actioncount == 0) {  /* No files given: read stdin */
  283.          full_output = verbose || do_list || do_symtab;
  284.  
  285.          fprintf(list_fd,"%s%s%s",
  286.    full_output?"\n":"",
  287.    VERSION_NUMBER,
  288.    full_output?"\n":"");
  289.  
  290.   if( must_open_outfile )
  291.       open_outfile(out_fname);
  292.  
  293.   if(make_project_file) {
  294.         projfile = STDIN_PROJ_FILENAME;
  295.         if( (project_fd = fopen(projfile,"w")) == NULL) {
  296.    fprintf(stderr,
  297.     "\nCannot open %s for output\n",projfile);
  298.         }
  299.         else {
  300.    if(verbose)
  301.      fprintf(stderr,
  302.     "\nProject file is %s\n",projfile);
  303.         }
  304.   }
  305.  
  306.   input_fd = stdin;
  307.  
  308.   src_file_in("std_input");
  309.  }
  310.  wrapup();
  311.  fprintf(list_fd,"\n");
  312.  
  313.  if(show_resources)
  314.      resource_summary();
  315.  
  316.  exit(0);
  317.  return 0;/* make lint happy */
  318. }
  319.  
  320. PRIVATE void
  321. src_file_in(infile)
  322.      char *infile;  /* input filename */
  323. {
  324.  note_filename(infile);
  325.  
  326.  init_scan();
  327.  init_parser();
  328.  
  329.  (void) yyparse();
  330.  
  331.  finish_scan();
  332.  
  333.  if(make_project_file) {
  334.     proj_file_out(project_fd);
  335.     (void) fclose(project_fd);
  336.  }
  337.  
  338.  error_summary(infile);
  339. }
  340.  
  341.  
  342. PRIVATE void
  343. error_summary(fname)  /* Print out count of errors in file */
  344.  char *fname;
  345. {
  346.  FILE *fd = list_fd;
  347.  
  348.  if(full_output)
  349.    fprintf(fd,"\n");
  350.  
  351.  if(port_check && tab_count != 0) {
  352.   ++warning_count;
  353.   fprintf(list_fd,
  354.   "\n Warning: file contains tabs.  May not be portable.\n");
  355.  }
  356.  
  357.  if(full_output || error_count != 0)
  358.    fprintf(fd,"\n %u syntax error%s detected in file %s",
  359.    error_count, error_count==1? "":"s",
  360.    fname);
  361.  
  362.  if(warning_count != 0)
  363.   fprintf(fd,"\n %u warning%s issued in file %s",
  364.    warning_count, warning_count==1? "":"s",
  365.    fname);
  366.  if(full_output)
  367.    fprintf(fd,"\n");
  368.  
  369.  error_count = 0;
  370.  warning_count = 0;
  371. }
  372.  
  373. void
  374. print_a_line(fd,line,num)  /* Print source line with line number */
  375.  FILE *fd;
  376.  char *line;
  377.  unsigned num;
  378. {
  379.  fprintf(fd,"\n %6u %s",num,line);
  380. }
  381.  
  382.  
  383. void
  384. yyerror(s)
  385.  char *s;
  386. {
  387.  syntax_error(line_num,col_num,s);
  388. }
  389.  
  390.  
  391. void
  392. syntax_error(lineno,colno,s)  /* Syntax error message */
  393.  unsigned lineno,colno;
  394.  char *s;
  395. {
  396.  ++error_count;
  397.  error_message(lineno,colno,s,"Error");
  398. }
  399.  
  400. PRIVATE void
  401. error_message(lineno,colno,s,tag)
  402.  unsigned lineno,colno;
  403.  char *s,*tag;
  404. {
  405.  int icol;
  406.  extern unsigned prev_stmt_line_num; /* shared with advance.c */
  407.  
  408.    /* Print the character ^ under the column number.
  409.       But if colno == 0, error occurred in prior line.
  410.       If colno is NO_COL_NUM, then print message
  411.       without any column number given.
  412.     */
  413.  
  414.      if(colno == NO_COL_NUM) {
  415.       /* colno == NO_COL_NUM means don't give column number.*/
  416.   (void)flush_line_out(lineno);/* print line if not printed yet */
  417.   fprintf(list_fd,
  418.      "\n%s near line %u",tag,lineno);
  419.      }
  420.      else if(colno != 0) {
  421.    /* print line if not printed yet */
  422.   if( flush_line_out(lineno) ) {
  423.     /* If it was printed, put ^ under the col */
  424.       fprintf(list_fd,"\n%8s","");
  425.  
  426.       for(icol=1; icol<colno; icol++)
  427.    fprintf(list_fd," ");
  428.       fprintf(list_fd,"^");
  429.   }
  430.   fprintf(list_fd,
  431.      "\n%s near line %u col %u",tag,lineno,colno);
  432.      }
  433.      else {  /* colno == 0 */
  434.    /* print line if not printed yet */
  435.   (void) flush_line_out(prev_stmt_line_num);
  436.   fprintf(list_fd,
  437.      "\n%s near line %u",tag,prev_stmt_line_num);
  438.      }
  439.  
  440.  if(incdepth > 0) /* Append include-file name if we are in one */
  441.    fprintf(list_fd," file %s",current_filename);
  442.  
  443.  fprintf(list_fd,": %s",s); /* now append the message string */
  444. }
  445.  
  446.  
  447. void
  448. msg_tail(s)
  449.     char *s;
  450. {
  451.  fprintf(list_fd," %s",s);
  452. }
  453. void
  454. warning(lineno,colno,s)  /* Print warning message */
  455.  unsigned lineno,colno;
  456.  char *s;
  457. {
  458.  ++warning_count;
  459.  
  460.  error_message(lineno,colno,s,"Warning");
  461. }
  462.  
  463. void
  464. nonstandard(lineno,colno)
  465.      unsigned lineno,colno;
  466. {
  467.  ++warning_count;
  468.  error_message(lineno,colno,"Nonstandard syntax","Warning");
  469. }
  470.  
  471. void
  472. nonportable(lineno,colno,s) /* Print warning about nonportable construction */
  473.  unsigned lineno,colno;
  474.  char *s;
  475. {
  476.  ++warning_count;
  477.  error_message(lineno,colno,s,"Nonportable usage");
  478. }
  479.  
  480. /* get_env_options picks up any options defined in the
  481.  environment.  A switch or setting is defined according to
  482.  the value of an environment variable whose name is the switch
  483.  or setting name (uppercased), prefixed by the string
  484.  ENV_PREFIX (e.g.  FORCHEK_).  For settings and strsettings,
  485.  the value of the environment variable gives the value to be
  486.  used.  For switches, the environment variable is set to "0" or
  487.  "NO" to turn the switch off, or to any other value (including
  488.  null) to turn it on.
  489. */
  490.  
  491. PRIVATE void
  492. get_env_options()
  493. {
  494.  char env_option_name[32];
  495.  char *value;
  496.  int i;
  497.  for(i=0; i<NUM_SWITCHES; i++) {
  498.    /* Construct the env variable name for switch i */
  499.      make_env_name( env_option_name, switchopt[i].name);
  500.  
  501.    /* See if it is defined */
  502.      if( (value = getenv(env_option_name)) != (char *)NULL) {
  503.          *(switchopt[i].switchflag) =
  504.    !(strcmp(value,"0")==0 || strcmp(value,"NO")==0 );
  505.      }
  506.  
  507.  }
  508.  
  509.  for(i=0; i<NUM_SETTINGS; i++) {
  510.    /* Construct the env variable name for setting i */
  511.      make_env_name( env_option_name, setting[i].name);
  512.    /* See if it is defined */
  513.      if( (value = getenv(env_option_name)) != (char *)NULL) {
  514.   if(read_setting(value, setting[i].setvalue, setting[i].name,
  515.       setting[i].minlimit, setting[i].maxlimit) != 0)
  516.    fprintf(stderr,"Env setting garbled: %s=%s: ignored\n",
  517.     env_option_name,value);
  518.      }
  519.  }
  520.  
  521.  
  522.  for(i=0; i<NUM_STRSETTINGS; i++) {
  523.    /* Construct the env variable name for setting i */
  524.      make_env_name( env_option_name, strsetting[i].name);
  525.    /* See if it is defined */
  526.      if( (value = getenv(env_option_name)) != (char *)NULL) {
  527.       *(strsetting[i].strvalue) = value;
  528.  
  529.    /* Handle necessary action for  -out=listfile */
  530.   if(strsetting[i].strvalue == &out_fname)
  531.    must_open_outfile = TRUE;
  532.      }
  533.  }
  534. }
  535.  
  536.   /* Routine to concatenate ENV_PREFIX onto option name
  537.      and uppercase the result.
  538.   */
  539. PRIVATE void
  540. make_env_name( env_name, option_name)
  541.  char *env_name, *option_name;
  542. {
  543.     int i,c;
  544.  
  545.     strcat(strcpy(env_name,ENV_PREFIX),option_name);
  546.     for(i=sizeof(ENV_PREFIX)-1; (c=env_name[i]) != '\0'; i++) {
  547.  if( islower(c) )
  548.      env_name[i] = toupper(c);
  549.     }
  550. }
  551.  
  552.  
  553.  /* set_option processes an option from command line.  Argument s is
  554.     the option string. First s is compared against boolean switches
  555.     from list in switchopt[].  If s matches switch string,
  556.     corresponding flag is set to TRUE.  If no match, then s is compared
  557.     to the same switches prefixed by "no", and if match is found, then
  558.     flag is set to FALSE.  Finally, special flags are handled.  If still
  559.     no match, an error message is generated.
  560.   */
  561.  
  562. PRIVATE void
  563. set_option(s)
  564.  char *s;
  565. {
  566.  int i;
  567.  for(i=0; i<NUM_SWITCHES; i++) {
  568.      if( strncmp(s+1,switchopt[i].name,OPT_MATCH_LEN) == 0) {
  569.   *(switchopt[i].switchflag) = TRUE;
  570.   return;
  571.      }
  572.  }
  573.  
  574.   /* switch not found: look for noswitch flag */
  575.  if(strncmp(s+1,"no",2) == 0) {
  576.      for(i=0; i<NUM_SWITCHES; i++) {
  577.   if( strncmp(s+3,switchopt[i].name,OPT_MATCH_LEN) == 0) {
  578.       *(switchopt[i].switchflag) = FALSE;
  579.       return;
  580.   }
  581.      }
  582.  }
  583.  
  584.   /* Handle settings of form "-opt=number" */
  585.  for(i=0; i<NUM_SETTINGS; i++)
  586.      if( strncmp(s+1,setting[i].name,OPT_MATCH_LEN) == 0) {
  587.   char *numstr;
  588.  
  589.   numstr = s + (OPT_MATCH_LEN + 1);
  590.   while(*numstr != '\0')
  591.       if(*numstr++ == '=') /* Find the = sign */
  592.    break;
  593.  
  594.   if(read_setting(numstr, setting[i].setvalue, setting[i].name,
  595.       setting[i].minlimit, setting[i].maxlimit) != 0)
  596.    fprintf(stderr,"Setting garbled: %s: ignored\n",s);
  597.   return;
  598.      }
  599.  
  600.  
  601.   /* Handle settings of form "-opt=string" */
  602.  for(i=0; i<NUM_STRSETTINGS; i++)
  603.      if( strncmp(s+1,strsetting[i].name,OPT_MATCH_LEN) == 0) {
  604.   char *strstart;
  605.   strstart = s + (OPT_MATCH_LEN + 1);
  606.   while(*strstart != '=' && *strstart != '\0')
  607.    strstart++; /* Find the = sign */
  608.   if(*strstart == '\0') {
  609.       fprintf(stderr,"String setting missing: %s: ignored\n",s);
  610.       return;
  611.   }
  612.   else {
  613.       *(strsetting[i].strvalue) = ++strstart;
  614.   }
  615.    /* Handle necessary action for  -out=listfile */
  616.   if(strsetting[i].strvalue == &out_fname) {
  617.    must_open_outfile = TRUE;
  618.   }
  619.   return;
  620.      }
  621.  
  622.  
  623.   /* No match found: issue error message */
  624.  
  625.  fprintf(stderr,"\nUnknown commandline switch: %s\n",s);
  626. }
  627.  
  628.  
  629.  /* Routine to read integer setting from string s and check if valid */
  630.  
  631. PRIVATE int
  632. read_setting(s, setvalue, name, minlimit, maxlimit)
  633.  char *s;
  634.  int *setvalue;
  635.  char *name;
  636.  int minlimit, maxlimit;
  637. {
  638.  int given_val;
  639.  
  640.  if(*s == '\0' || sscanf(s,"%d", &given_val) == 0) {
  641.      return -1; /* error return: garbled setting */
  642.  }
  643.  else {  /* If outside limits, set to nearest limit */
  644.      int Ok=TRUE;
  645.      if(given_val < minlimit) {
  646.   given_val = minlimit;
  647.   Ok = FALSE;
  648.      }
  649.      else if(given_val > maxlimit) {
  650.   given_val = maxlimit;
  651.   Ok = FALSE;
  652.      }
  653.  
  654.      if(! Ok ) {
  655.   fprintf(stderr,"\nSetting: %s",name);
  656.   fprintf(stderr," outside limits %d to %d",
  657.     minlimit,maxlimit);
  658.   fprintf(stderr,": set to %d\n",given_val);
  659.      }
  660.  
  661.      *(setvalue) = given_val;
  662.  }
  663.  return 0;
  664. }
  665.  
  666. PRIVATE void
  667. open_outfile(s)  /* open the output file for listing */
  668.  char *s;
  669. {
  670.  char *fullname;  /* given name plus extension */
  671.  FILE *fd;
  672.  
  673.  must_open_outfile = FALSE; /* Turn off the flag */
  674.  
  675.  if(s == (char *) NULL || *s == '\0') {
  676.   return;  /* No filename: no action  */
  677.  }
  678.  
  679.  fullname = add_ext(s,DEF_LIST_EXTENSION);
  680.  if( (fd = fopen(fullname,"w")) == NULL) {
  681.   fprintf(stderr,"\nCannot open %s for output\n",fullname);
  682.  }
  683.  else {
  684.   fprintf(stderr,"\nOutput sent to file %s\n",fullname);
  685.   list_fd = fd;
  686.  }
  687. }
  688.  
  689.  
  690. PRIVATE void
  691. list_options() /* List all commandline options, strsettings, and settings */
  692. {
  693.  int i;
  694.   /* Note: Headings say "default" but to be accurate they
  695.      should say "current value".  This would be confusing. */
  696.  fprintf(stderr,"\nCommandline options [default]:");
  697.  for(i=0; i<NUM_SWITCHES; i++) {
  698.  
  699.    if( !debug_latest &&
  700.       strncmp(switchopt[i].explanation,"debug",5) == 0)
  701.      continue;  /* skip debug switches unless debug mode */
  702.  
  703.    fprintf(stderr,"\n\t%c[no]%s",OPT_PREFIX,switchopt[i].name);
  704.    fprintf(stderr," [%s]",*(switchopt[i].switchflag)? "yes": "no");
  705.    fprintf(stderr,": %s",switchopt[i].explanation);
  706.  }
  707.   /* String settings follow switches w/o their own heading */
  708.  for(i=0; i<NUM_STRSETTINGS; i++) {
  709.    if( !debug_latest &&
  710.       strncmp(strsetting[i].explanation,"debug",5) == 0)
  711.      continue;  /* skip debug settings unless debug mode */
  712.  
  713.    fprintf(stderr,"\n\t%c%s=str ",OPT_PREFIX,strsetting[i].name);
  714.    fprintf(stderr,"[%s]",
  715.     *(strsetting[i].strvalue)? *(strsetting[i].strvalue): "NONE");
  716.    fprintf(stderr,": %s",strsetting[i].explanation);
  717.  }
  718.  
  719.  fprintf(stderr,"\nSettings (legal range) [default]:");
  720.  for(i=0; i<NUM_SETTINGS; i++) {
  721.  
  722.    if( !debug_latest &&
  723.       strncmp(setting[i].explanation,"debug",5) == 0)
  724.      continue;  /* skip debug settings unless debug mode */
  725.  
  726.    fprintf(stderr,"\n\t%c%s=dd ",OPT_PREFIX,setting[i].name);
  727.    fprintf(stderr,"(%d to %d) ",setting[i].minlimit,
  728.     setting[i].maxlimit);
  729.    fprintf(stderr,"[%d]",*(setting[i].setvalue));
  730.    fprintf(stderr,": %s",setting[i].explanation);
  731.  }
  732.  
  733.     fprintf(stderr,
  734.      "\n(First %d chars of option name significant)\n",OPT_MATCH_LEN);
  735. }
  736.  
  737.  
  738. PRIVATE void
  739. wrapup() /* look at cross references, etc. */
  740. {
  741.  check_comlists();
  742.  check_arglists();
  743. }
  744.  
  745.  
  746.   /* Adds default extension to source file name if
  747.      none is present, and returns a pointer to the
  748.      new name.  If extension was added, space is allocated
  749.      for the new name.  If not, simply  returns pointer
  750.      to original name.
  751.   */
  752. char *
  753. add_ext(s,ext)
  754.  char *s,*ext;
  755. {
  756.  int i,len=strlen(s);
  757.  char *newname;
  758.  
  759.   /* Search backwards till find the dot, but do not
  760.      search past / (Unix) or ] (VMS) directory delimiter
  761.   */
  762.  for(i=len; i>0; i--) {
  763.      if(s[i] == '.' || s[i] == '/' || s[i] == ']')
  764.   break;
  765.  }
  766.  
  767.  if(s[i] != '.') {
  768.      newname = (char *) malloc( (unsigned)(len+strlen(ext)+1) );
  769.      (void)strcpy(newname,s);
  770.      (void)strcat(newname,ext);
  771.      s = newname;
  772.  }
  773.  
  774.  return s;
  775. }
  776.  
  777.   /* Adds default extension to source file name, replacing
  778.      any that is present, and returns a pointer to the
  779.      new name.  Space is allocated for the new name.
  780.   */
  781. char *
  782. new_ext(s,ext)
  783.  char *s,*ext;
  784. {
  785.  int i,len=strlen(s);
  786.  char *newname;
  787.  
  788.   /* Search backwards till find the dot, but do not
  789.      search past / (Unix) or ] (VMS) directory delimiter
  790.   */
  791.  for(i=len; i>0; i--) {
  792.      if(s[i] == '.' || s[i] == '/' || s[i] == ']')
  793.   break;
  794.  }
  795.  
  796.  if(s[i] != '.') { /* No extension present: add one on */
  797.      newname = (char *) malloc( (unsigned)(len+strlen(ext)+1) );
  798.      (void)strcpy(newname,s);
  799.      (void)strcat(newname,ext);
  800.  }
  801.  else {   /* Extension present: replace it */
  802.      newname = (char *) malloc( (unsigned)(i+strlen(ext)+1) );
  803.      (void)strncpy(newname,s,i);
  804.      (void)strcpy(newname+i,ext);
  805.  }
  806.  
  807.  return newname;
  808. }
  809.  
  810. PRIVATE int
  811. cistrcmp(s1,s2)   /* case-insensitive strcmp */
  812.      char *s1,*s2;
  813. {
  814.   while( (isupper(*s1)?tolower(*s1):*s1) == (isupper(*s2)?tolower(*s2):*s2) ) {
  815.     if(*s1 == '\0')
  816.       return 0;
  817.     ++s1; ++s2;
  818.   }
  819.   return *s1 - *s2;
  820. }
  821.  
  822. int
  823. has_extension(name,ext)  /* true if name ends in ext */
  824.   char *name,*ext;
  825. {
  826.   int stem_len = strlen(name) - strlen(ext);
  827.   if( stem_len >= 0 && cistrcmp(name+stem_len,ext) == 0 )
  828.     return TRUE;
  829.   else
  830.     return FALSE;
  831. }
  832.  
  833. PRIVATE void
  834. resource_summary()
  835. {
  836.      fprintf(list_fd,
  837.     "\nMax namestring space used = %lu local, %lu global out of %lu chars",
  838.    max_loc_strings,
  839.    max_glob_strings,
  840.    (unsigned long)STRSPACESZ);
  841.      fprintf(list_fd,
  842.   "\nMax local symbols used =  %lu out of %lu available",
  843.    max_loc_symtab,
  844.    (unsigned long)LOCSYMTABSZ);
  845.      fprintf(list_fd,
  846.   "\nMax global symbols used = %lu out of %lu available",
  847.    max_glob_symtab,
  848.    (unsigned long)GLOBSYMTABSZ);
  849.      fprintf(list_fd,
  850.   "\nMax tokenlist space used = %lu out of %lu available",
  851.    max_token_space,
  852.    (unsigned long)TOKENSPACESZ);
  853.      fprintf(list_fd,
  854.   "\nIdentifier hashtable size = %6lu",
  855.    (unsigned long)HASHSZ);
  856.      fprintf(list_fd,
  857.   "\nKeyword hashtable size = %6lu",
  858.    (unsigned long)KEYHASHSZ);
  859. #ifdef COUNT_REHASHES
  860.      fprintf(list_fd,
  861.   "\nIdentifier rehash count = %6lu",
  862.    rehash_count);
  863. #endif
  864.      fprintf(list_fd,
  865.   "\nIntrinsic function hashtable size=%6lu, clash count=%lu",
  866.    (unsigned long)INTRINS_HASHSZ,
  867.    intrins_clashes);
  868.      fprintf(list_fd,"\n\n");
  869. }
  870.